package com.opages.netty.demo.lesson.dark_horse2;

import com.opages.netty.demo.lesson.ByteBufferUtil;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;

/**
 * @author daizhong.liu
 * @create 2021-12-28 15:43
 **/
@Slf4j
public class Socket {
    @Test
    public void testBIOServer() {
        try (
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ){
            serverSocketChannel.bind(new InetSocketAddress(8889));
            List<SocketChannel> socketChannels = new ArrayList<>();
            ByteBuffer buffer = ByteBuffer.allocate(32);
            while (true) {
                SocketChannel socketChannel = serverSocketChannel.accept();
                socketChannels.add(socketChannel);
                for (SocketChannel channel : socketChannels) {
                    channel.read(buffer);
                    buffer.flip();
                    ByteBufferUtil.debugAll(buffer);
                    buffer.clear();
                    System.out.println("after reading");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void testNIOServer() {
        try (
                ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ){
            serverSocketChannel.bind(new InetSocketAddress(8889));
            List<SocketChannel> socketChannels = new ArrayList<>();
            ByteBuffer buffer = ByteBuffer.allocate(32);
            //非阻塞状态下会一下遍历使用cpu一直处理忙碌状态
            while (true) {
                serverSocketChannel.configureBlocking(false);
                SocketChannel socketChannel = serverSocketChannel.accept();
                if(socketChannel != null) {
                    socketChannels.add(socketChannel);
                    System.err.println("存在连接!");
                }
                for (SocketChannel channel : socketChannels) {
                    channel.configureBlocking(false);
                    int read = channel.read(buffer);
                    if(read > 0) {
                        buffer.flip();
                        ByteBufferUtil.debugAll(buffer);
                        buffer.clear();
                        System.out.println("after reading");
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testSelectServer() {
        try(ServerSocketChannel server = ServerSocketChannel.open()) {
            server.configureBlocking(false);
            server.bind(new InetSocketAddress(8889));
            Selector selector = Selector.open();
            server.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                // 若没有事件就绪，线程会被阻塞，反之不会被阻塞。从而避免了CPU空转
                // 返回值为就绪的事件个数
                int selectCle = selector.select();
                log.debug("存在事件:{}",selectCle);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while(iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    if(selectionKey.isAcceptable()) {
                        log.debug("客户端连接...");
                        ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
                        SocketChannel socketChannel = channel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector,SelectionKey.OP_READ);
                    }
                    if(selectionKey.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer readBuffer = ByteBuffer.allocate(16);
                        int read = socketChannel.read(readBuffer);
                        if(read > 0) {
                            log.debug("存在传输数据...");
                            ByteBufferUtil.debugAll(readBuffer);
                        }
                    }
                    //处理完后要移除，否则会存在一直遍历的问题
                    iterator.remove();
                }
            }

        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Test
    public void testWriteServer() {
        try(ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
            serverSocketChannel.bind(new InetSocketAddress(8889));
            serverSocketChannel.configureBlocking(false);
            Selector selector = Selector.open();
            serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
            while (true) {
                selector.select();
                Iterator<SelectionKey> iteratorKeys = selector.selectedKeys().iterator();
                while (iteratorKeys.hasNext()) {
                    SelectionKey key = iteratorKeys.next();
                    iteratorKeys.remove();
                    if(key.isAcceptable()) {
                        log.debug("客户端连接事件...");
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        serverSocketChannel.register(selector,SelectionKey.OP_READ);
//                        StringBuilder sb = new StringBuilder();
//                        for (int i = 0; i < 5000; i++) {
//                            sb.append("_"+i);
//                        }
//                        log.debug("向客户端写内容：\n{}\n",sb.toString());
//                        ByteBuffer contentBuffer = ByteBuffer.wrap(sb.toString().getBytes(CharsetUtil.UTF_8));
//                        socketChannel.write(contentBuffer);
//                        if(contentBuffer.hasRemaining()) {
//                            log.debug("空间不足，注册写事件，由另一个线程执行剩余的写操作.");
//                            socketChannel.configureBlocking(false);
//                            socketChannel.register(selector,SelectionKey.OP_WRITE,contentBuffer);
//                        }
                    }
//                    if(key.isWritable()) {
//                        log.debug("写客户端事件...");
//                        SocketChannel socketChannel = (SocketChannel) key.channel();
//                        ByteBuffer attachment = (ByteBuffer)key.attachment();
//                        socketChannel.write(attachment);
//                        if(!attachment.hasRemaining()) {
//                            log.debug("数据写完，解除客户端写事件..");
//                            key.attach(null);
//                            key.interestOps(0);
//                        }
//                    }
                    if(key.isReadable()) {
                        log.debug("读客户端事件...");
//                        SocketChannel socketChannel = (SocketChannel) key.channel();
//                        ByteBuffer attachment = (ByteBuffer)key.attachment();
////                        ByteBuffer readBuffer = ByteBuffer.allocate(32);
//                        int read = socketChannel.read(attachment);
//                        if(read == -1) {
////                            key.cancel();
////                            socketChannel.close();
//                            log.debug("客户端发送过来的内容：");
//                            ByteBufferUtil.debugAll(attachment);
//                        }else {
//                            //数据太多，buffer装不下
//                            if(attachment.position() == attachment.limit()) {
//                                ByteBuffer newBuffer = ByteBuffer.allocate(attachment.capacity() * 2);
//                                attachment.flip();
//                                newBuffer.put(attachment);
//                                key.attach(newBuffer);
//                            }
//                        }
                    }
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testSimpleClient() {
        try (SocketChannel socketChannel = SocketChannel.open()) {
            socketChannel.connect(new InetSocketAddress("127.0.0.1",8889));
            log.debug("连接成功..");
            socketChannel.write(ByteBuffer.wrap("hello nio...".getBytes(CharsetUtil.UTF_8)));
            log.debug("写入数据...");
            System.in.read();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testBIOClient() {
        try (SocketChannel socketChannel = SocketChannel.open()) {
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("127.0.0.1",8889));
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Selector selector = Selector.open();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(32);
                        socketChannel.register(selector, SelectionKey.OP_READ, byteBuffer);
                        while (true) {
                            selector.select();
                            Iterator<SelectionKey> iteratorKeys = selector.selectedKeys().iterator();
                            while (iteratorKeys.hasNext()) {
                                SelectionKey key = iteratorKeys.next();
                                iteratorKeys.remove();
                                if (key.isReadable()) {
                                    ByteBuffer attachment = (ByteBuffer) key.attachment();
                                    int read = socketChannel.read(attachment);
                                    if (read == -1) {
                                        log.debug("读取完成..");
                                        ByteBufferUtil.debugAll(attachment);
                                    } else {
                                        if (attachment.position() == attachment.limit()) {
                                            ByteBuffer newBuffer = ByteBuffer.allocate(attachment.capacity() * 2);
                                            // 将旧buffer中的内容放入新的buffer中
                                            attachment.flip();
                                            newBuffer.put(attachment);
                                            // 将新buffer放到key中作为附件
                                            key.attach(newBuffer);
                                        }
                                    }
                                }
                            }
                        }
                    }catch(Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            //写数据
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()) {
                String content = scanner.nextLine();
                socketChannel.write(ByteBuffer.wrap(content.getBytes(CharsetUtil.UTF_8)));
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
